﻿using System.Collections.Generic;
using DimOS.Constants;
using DimOS.DimSH;
using DimOS.Internals;
using DimOS.Plugs;
using DateTime = DimOS.Plugs.DateTime;
using String = DimOS.Plugs.String;

namespace DimOS.DimFS
{
    //Represents a single node (file/folder) in File System
    public class Node
    {
        public string Name { get; set; }                    //Name of the node (no path separator or "hidden")

        public Node Parent { get; set; }                    //Link to the parent node (null for the root node)

        public Permissions Permissions { get; set; }        //Basic permissions for the node (and descendants)

        public Attributes Attributes { get; set; }          //Simple attributes for the node (and descendants)

        private Content _content;
        public Content Content                              //Content of the node (either child nodes or data)
        {
            get { return _content; }
            set { _content = value; Content.Accessed =
                Content.Modified = DateTime.Now; }
        }                

        public int Size { get { return Content.Size(); } }  //Size of the node's Content (calculated on "get")

        public DateTime Created { get; private set; }       //DateTime value showing when the node was created

        public DateTime Modified                            //DateTime value showing when Content was modified
        { get { return Content.Modified; } }

        public DateTime Accessed                            //DateTime value showing when Content was accessed
        { get { return Content.Accessed; } }      


        public Node(string name, Node parent, Permissions permissions, Attributes attributes, Content content)
        {
            Created = Content.Modified =
                Content.Accessed = DateTime.Now;
            Name = name;
            Parent = parent;
            Permissions = permissions;
            Attributes = attributes;
            Content = content;
        }

        public List<Node> FindChildren(string name, bool includeHidden = true)
        {
            var list = new List<Node>();
            bool globbing = (name.IndexOf(Tokens.WildcardAny) != -1 ||
                             name.IndexOf(Tokens.WildcardOne) != -1);
            for (int i = 0; i < Content.Children.Count; i++)
                if (Content.Children[i].Name.Matches(name) && !(globbing &&
                    !includeHidden && Content.Children[i].Name[0] == Tokens.Hidden &&
                    (name[0] == Tokens.WildcardAny || name[0] == Tokens.WildcardOne)))
                {
                    list.Add(Content.Children[i]);
                    if (!globbing) return list;
                }
            return list;
        }

        public List<Node> FindChildren(bool includeHidden = true)
        {
            var list = new List<Node>();
            for (int i = 0; i < Content.Children.Count; i++)
                if (!(!includeHidden && Content.Children[i].Name[0] == Tokens.Hidden))
                    list.Add(Content.Children[i]);
            return list;
        }

        public bool ParentExists(Node parent)
        {
            if (Parent == null) return false;
            var node = this;
            while (node != null) { if (node == parent) return true; node = node.Parent; }
            return false;
        }

        public bool Destroy()
        {
            if (Parent == null) return false;
            for (int i = 0; i < Parent.Content.Children.Count; i++)
                if (this == Parent.Content.Children[i])
                { Parent.Content.Children.RemoveAt(i); return true; }
            return false;
        }

        public Node Clone(Account owner, Node newParent, string newName = "", bool deep = false)
        { 
            var node = deep ?
            DeepCopy(owner, this, newParent, newName):
            ShallowCopy(this, newParent, newName);
            newParent.Content.Children.Add(node);
            return node;
        }

        private static Node ShallowCopy(Node source, Node newParent, string newName = "")
        {
            var name = String.Replace(String.Replace(newName,
                Tokens.WildcardAny.ToString(), ""),Tokens.WildcardOne.ToString(), "");
            return new Node(name == "" ? source.Name : name, newParent,
                source.Permissions, source.Attributes, source.Content);
        }

        private static Node DeepCopy(Account owner, Node source, Node newParent, string newName = "")
        {
            var content = new Content(Settings.AdvancedPaths
                || source.Content.Children != null
                ? new List<Node>() : null,
                source.Content.Data);
            var name = String.Replace(String.Replace(newName,
                Tokens.WildcardAny.ToString(), ""),Tokens.WildcardOne.ToString(), "");
            var node = new Node(name == "" ? source.Name : name,
                newParent, owner == Account.Root ?
                new Permissions(owner,
                    source.Permissions.Read,
                    source.Permissions.Write,
                    source.Permissions.Change,
                    source.Permissions.Delete)
                    : Permissions.Default(owner),
                new Attributes(false, false, false,
                    source.Attributes.Settings,
                    source.Attributes.Journal,
                    source.Attributes.HelpFile,
                    source.Attributes.Query),
                content);

            if (source.Content.Children != null)
                for (int i = 0; i < source.Content.Children.Count; i++)
                    content.Children.Add(DeepCopy(owner, source.Content.Children[i], node));

            return node;
        }
    }
}

